local oo = require("SharedLibs.BaseLib")

Debugger = require("SharedLibs.Debugger")

IReadOnlyProperty = require("SharedLibs.Signal.IReadOnlyProperty")
ISignal = require("SharedLibs.Signal.ISignal")
Signal = require("SharedLibs.Signal.Signal")

--[[
	把两个属性组合成一个新属性。
	和TupleProperty类似, 但是TupleProperty组合出来的是元组属性, 任何
	一个子属性变化都会触发changed事件. 
	
	而本类组合出的还是普通属性, 只有当组合后的属性值变化时才会触发changed事件.
	
	各种二元Condition操作, 例如Or, And, XOr都是用该类实现的

	注意: Map后的Property每次GetValue时都要执行一次Map函数. 如果Map的嵌套层次很高, 有可能有性能问题
	如果出现性能问题, 上层可以自行实现cache, 也就是如果parent的值没有变化, 并且parent不是table或Instance.
	就可以不用Map, 直接返回cache的值
]] 
local cls = oo.class();
cls:implements(IReadOnlyProperty);

function cls:constructor(p1, p2, mapper, name)
	assert(p1 and p2 and mapper);
	
	self.name = name or oo.GetClassName(cls);
	
	self._changed = Signal.new("PropertyChanged");
	self._listenerCount = 0;
	
	self._p1 = p1;
	self._p2 = p2;
	
	self._mapper = mapper;

	local pV1 = p1:GetValue();
	local pV2 = p2:GetValue();
	self._val = mapper(pV1, pV2);

end

function cls:GetValue()
	local pV1 = self._p1:GetValue();
	local pV2 = self._p2:GetValue();

	local val = self._mapper(pV1, pV2);
	if val ~= self._val then
		self._val = val;
		self._changed:Emit(val);
	end
	return val;
end

function cls:On(handler, retDestroyer)
	self._listenerCount = self._listenerCount + 1;
	self._changed:On(handler);

	if self._listenerCount == 1 then
		self._onP1Changed = function(value)
			local pV1 = value;
			local pV2 = self._p2:GetValue();
			local newVal = self._mapper(pV1, pV2);
			if newVal ~= self._val then
				self._val = newVal;
				self._changed:Emit(newVal);
			end
		end			
		self._onP2Changed = function(value)
			local pV2 = value;
			local pV1 = self._p1:GetValue();
			local newVal = self._mapper(pV1, pV2);
			if newVal ~= self._val then
				self._val = newVal;
				self._changed:Emit(newVal);
			end
		end
		
		self._p1:On(self._onP1Changed);
		self._p2:On(self._onP2Changed);
	end

	if retDestroyer then return function() self:Off(handler); end end
end

function cls:Off(handler)
	if self._listenerCount == 0 then return end;
		
	self._listenerCount = self._listenerCount - 1;
	assert(self._listenerCount >= 0);
	self._changed:Off(handler);

	if self._listenerCount == 0 then
		self._p1:Off(self._onP1Changed);
		self._p2:Off(self._onP2Changed);
		self._onP1Changed = nil;
		self._onP2Changed = nil;
	end
end

function cls:Clear()
	local needOff = self._listenerCount ~= 0;

	self._listenerCount = 0;
	self._changed:Clear();

	if needOff then
		self._p1:Off(self._onP1Changed);
		self._p2:Off(self._onP2Changed);
		self._onP1Changed = nil;
		self._onP2Changed = nil;
	end
end

return cls;
